package hello.java.app2;

import io.grpc.*;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class GrpcWrapper {
    private static ManagedChannel channel;
    private static final Logger logger = LoggerFactory.getLogger(GrpcWrapper.class);
    private static String grpcUrl;
    private static TestClient client;
    private static boolean isReconnecting = false;
    private static final Random random = new Random();

    public static void reconnect() {
        if (channel != null) {
            channel.shutdownNow();
        }
        if (isReconnecting) {
            return;
        }
        isReconnecting = true;
        try {
            var time = (random.nextInt(6) + 1) * 1000;
            logger.info("reconnect after " + time + "...grpcUrl:" + grpcUrl);
            var future = CompletableFuture.runAsync(() -> {
                try {
                    // 支持随机时间重连
                    Thread.sleep(time);
                    GrpcWrapper.run(grpcUrl);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            future.join();
        } finally {
            logger.info("reconnect end");
            isReconnecting = false;
        }
    }

    public static void run(String target) throws Exception {
        grpcUrl = target;
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[] { new TrustAllCerts() }, new SecureRandom());
        SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient();
        sslContextBuilder.trustManager(new TrustAllCerts());
        SslContext sslContext2 = sslContextBuilder.build();
        ManagedChannel channel = NettyChannelBuilder.forTarget(target)
                .useTransportSecurity()
                .sslContext(sslContext2)
                .build();
        // 这里使用nignx反代grpc出现服务侧无法读取上传的语音文件，因此采用TLS直连服务器的模式
        // channel = Grpc.newChannelBuilder(target, InsecureChannelCredentials.create())
        // .build();
        try {
            client = new TestClient(channel);

            var thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        logger.info("send sip audio start");
                        client.sendSipAudio();
                        logger.info("send sip audio end");
                    } catch (Exception e) {
                        logger.warn(e.toString());
                        GrpcWrapper.reconnect();
                    }
                }
            });
            thread.start();

            var thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        logger.info("send dtmf start");
                        client.sendDtmf();
                        logger.info("send dtmf end");
                    } catch (Exception e) {
                        logger.warn(e.toString());
                        GrpcWrapper.reconnect();
                    }
                }
            });
            thread2.start();

            // client.sendFileAudio();
            logger.info("grpc init finish..");
        } catch (Exception ex) {
            logger.info(ex.toString());
            channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
            channel = null;
            GrpcWrapper.reconnect();
        }
    }

    public static void stop() {
        try {
            channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static TestClient getClient() {
        return client;
    }
}
